package com.xiaopo.flying.sticker;

import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;

/**
 * @author wupanjie
 */
public abstract class Sticker {

    public @interface Position {
        int CENTER = 1;
        int TOP = 1 << 1;
        int LEFT = 1 << 2;
        int RIGHT = 1 << 3;
        int BOTTOM = 1 << 4;
    }

    public static final int MSCALE_X = 0;   //!< use with getValues/setValues
    public static final int MSKEW_X = 1;   //!< use with getValues/setValues
    public static final int MTRANS_X = 2;   //!< use with getValues/setValues
    public static final int MSKEW_Y = 3;   //!< use with getValues/setValues
    public static final int MSCALE_Y = 4;   //!< use with getValues/setValues
    public static final int MTRANS_Y = 5;   //!< use with getValues/setValues
    public static final int MPERSP_0 = 6;   //!< use with getValues/setValues
    public static final int MPERSP_1 = 7;   //!< use with getValues/setValues
    public static final int MPERSP_2 = 8;   //!< use with getValues/setValues
    private float[] matrixValues = new float[9];
    private final float[] unrotatedWrapperCorner = new float[8];
    private final float[] unrotatedPoint = new float[2];
    private final float[] boundPoints = new float[8];
    private final float[] mappedBounds = new float[8];
    private final RectFloat trappedRect = new RectFloat();
    private final Matrix matrix = new Matrix();
    private boolean isFlippedHorizontally;
    private boolean isFlippedVertically;

    public boolean isFlippedHorizontally() {
        return isFlippedHorizontally;
    }

    public Sticker setFlippedHorizontally(boolean flippedHorizontally) {
        isFlippedHorizontally = flippedHorizontally;
        return this;
    }

    public boolean isFlippedVertically() {
        return isFlippedVertically;
    }

    public Sticker setFlippedVertically(boolean flippedVertically) {
        isFlippedVertically = flippedVertically;
        return this;
    }

    public Matrix getMatrix() {
        return matrix;
    }

    public Sticker setMatrix(Matrix matrix) {
        this.matrix.setMatrix(matrix);
        return this;
    }

    public abstract void drawPixelMap(Canvas canvas);

    public abstract void draw(Canvas canvas);

    public abstract int getWidth();

    public abstract int getHeight();

    public abstract Sticker setDrawable(Element drawable);

    public abstract Element getDrawable();

    public abstract Sticker setAlpha(int alpha);

    public float[] getBoundPoints() {
        float[] points = new float[8];
        getBoundPoints(points);
        return points;
    }

    public void getBoundPoints(float[] points) {
        if (!isFlippedHorizontally) {
            if (!isFlippedVertically) {
                points[0] = 0f;
                points[1] = 0f;
                points[2] = getWidth();
                points[3] = 0f;
                points[4] = 0f;
                points[5] = getHeight();
                points[6] = getWidth();
                points[7] = getHeight();
            } else {
                points[0] = 0f;
                points[1] = getHeight();
                points[2] = getWidth();
                points[3] = getHeight();
                points[4] = 0f;
                points[5] = 0f;
                points[6] = getWidth();
                points[7] = 0f;
            }
        } else {
            if (!isFlippedVertically) {
                points[0] = getWidth();
                points[1] = 0f;
                points[2] = 0f;
                points[3] = 0f;
                points[4] = getWidth();
                points[5] = getHeight();
                points[6] = 0f;
                points[7] = getHeight();
            } else {
                points[0] = getWidth();
                points[1] = getHeight();
                points[2] = 0f;
                points[3] = getHeight();
                points[4] = getWidth();
                points[5] = 0f;
                points[6] = 0f;
                points[7] = 0f;
            }
        }
    }

    public float[] getMappedBoundPoints() {
        float[] dst = new float[8];
        getMappedPoints(dst, getBoundPoints());
        return dst;
    }

    public float[] getMappedPoints(float[] src) {
        float[] dst = new float[src.length];
        matrix.mapPoints(dst, src);
        return dst;
    }

    public void getMappedPoints(float[] dst, float[] src) {
        matrix.mapPoints(dst, src);
    }

    public RectFloat getBound() {
        RectFloat bound = new RectFloat();
        getBound(bound);
        return bound;
    }

    public void setRectFloat(RectFloat dst, float left, float top, float right, float bottom) {
        dst.left = left;
        dst.right = right;
        dst.top = top;
        dst.bottom = bottom;
    }

    public void getBound(RectFloat dst) {
        setRectFloat(dst, 0, 0, getWidth(), getHeight());
    }

    public RectFloat getMappedBound() {
        RectFloat dst = new RectFloat();
        getMappedBound(dst, getBound());
        return dst;
    }

    public void getMappedBound(RectFloat dst, RectFloat bound) {
        matrix.mapRect(dst, bound);
    }

    public Point getCenterPoint() {
        Point center = new Point();
        getCenterPoint(center);
        return center;
    }

    public void getCenterPoint(Point dst) {
        dst.position[0] = getWidth() * 1f / 2;
        dst.position[1] = getHeight() * 1f / 2;
    }

    public Point getMappedCenterPoint() {
        Point pointF = getCenterPoint();
        getMappedCenterPoint(pointF, new float[2], new float[2]);
        return pointF;
    }

    public void getMappedCenterPoint(Point dst, float[] mappedPoints,
                                     float[] src) {
        getCenterPoint(dst);
        src[0] = dst.getPointX();
        src[1] = dst.getPointY();
        getMappedPoints(mappedPoints, src);
        dst.position[0] = mappedPoints[0];
        dst.position[1] = mappedPoints[1];
    }

    public float getCurrentScale() {
        return getMatrixScale(matrix);
    }

    public float getCurrentHeight() {
        return getMatrixScale(matrix) * getHeight();
    }

    public float getCurrentWidth() {
        return getMatrixScale(matrix) * getWidth();
    }

    /**
     * This method calculates scale value for given Matrix object.
     *
     * @param matrix
     * @return float
     */
    public float getMatrixScale(Matrix matrix) {
        return (float) Math.sqrt(Math.pow(getMatrixValue(matrix, MSCALE_X), 2) + Math.pow(
            getMatrixValue(matrix, MSKEW_Y), 2));
    }

    /**
     * 获取当前角度
     *
     * @return - current image rotation angle.
     */
    public float getCurrentAngle() {
        return getMatrixAngle(matrix);
    }

    /**
     * This method calculates rotation angle for given Matrix object.
     *
     * @param matrix
     * @return float
     */
    public float getMatrixAngle(Matrix matrix) {
        return (float) Math.toDegrees(-(Math.atan2(getMatrixValue(matrix, MSKEW_X),
            getMatrixValue(matrix, MSCALE_X))));
    }

    public float getMatrixValue(Matrix matrix, int valueIndex) {
        matrixValues = matrix.getData();
        return matrixValues[valueIndex];
    }

    public boolean contains(float x, float y) {
        return contains(new float[]{x, y});
    }

    public boolean contains(float[] point) {
        Matrix tempMatrix = new Matrix();
        tempMatrix.setRotate(-getCurrentAngle());
        getBoundPoints(boundPoints);
        getMappedPoints(mappedBounds, boundPoints);
        tempMatrix.mapPoints(unrotatedWrapperCorner, mappedBounds);
        tempMatrix.mapPoints(unrotatedPoint, point);
        StickerUtils.trapToRect(trappedRect, unrotatedWrapperCorner);

        return contains(trappedRect, unrotatedPoint[0], unrotatedPoint[1]);
    }

    public boolean contains(RectFloat rectFloat, float x, float y) {
        return rectFloat.left < rectFloat.right && rectFloat.top < rectFloat.bottom  // check for empty first
            && x >= rectFloat.left && x < rectFloat.right && y >= rectFloat.top && y < rectFloat.bottom;
    }

    public void release() {
    }
}
